(function() {
  var __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  window.logger = {
    enabled: true,
    log: function(message) {
      if (console && console.log) {
        return console.log(message);
      } else {
        return alert(message);
      }
    }
  };

  window.Shader = (function() {

    function Shader(context) {
      this.context = context;
      this.shaderProgram = this.context.createProgram();
      this.shaders = [];
    }

    Shader.prototype.addShader = function(source, type, typeString) {
      var shader;
      shader = this.context.createShader(type);
      this.context.shaderSource(shader, source);
      this.context.compileShader(shader);
      if (!this.context.getShaderParameter(shader, this.context.COMPILE_STATUS)) {
        logger.log("ERROR IN " + typeString + " SHADER : " + this.context.getShaderInfoLog(shader));
        false;
      }
      this.shaders.push(shader);
      return shader;
    };

    Shader.prototype.attachShader = function(shader) {
      return this.context.attachShader(this.shaderProgram, shader);
    };

    Shader.prototype.link = function(callback) {
      return this.context.linkProgram(this.shaderProgram);
    };

    Shader.prototype.use = function() {
      return this.context.useProgram(this.shaderProgram);
    };

    Shader.prototype.getUniformLocation = function(loc) {
      return this.context.getUniformLocation(this.shaderProgram, loc);
    };

    Shader.prototype.getAttribLocation = function(loc) {
      return this.context.getAttribLocation(this.shaderProgram, loc);
    };

    return Shader;

  })();

  window.Texture = (function() {

    function Texture(context, url, callback) {
      var self;
      this.context = context;
      this.url = url;
      this.image = new Image();
      self = this;
      this.image.onload = function() {
        var GL, texture;
        GL = self.context;
        texture = GL.createTexture();
        GL.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, true);
        GL.bindTexture(GL.TEXTURE_2D, texture);
        GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, this);
        GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR);
        GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST_MIPMAP_LINEAR);
        GL.generateMipmap(GL.TEXTURE_2D);
        self.texture = texture;
        if (typeof callback === "function") {
          return callback();
        }
      };
      this.image.src = this.url;
    }

    return Texture;

  })();

  window.Scene3D = (function(_super) {

    __extends(Scene3D, _super);

    function Scene3D(canvasId) {
      var GL;
      this.canvasId = canvasId;
      Scene3D.__super__.constructor.apply(this, arguments);
      this.canvas = document.getElementById(this.canvasId);
      this.context = this.canvas.getContext("webgl", {
        antialias: true
      }) || this.canvas.getContext("experimental-webgl", {
        antialias: true
      });
      this.shapes = [];
      animation.bind(this, this.animationLoop);
      GL = this.context;
      GL.enable(GL.DEPTH_TEST);
      GL.depthFunc(GL.LEQUAL);
      GL.clearColor(0.0, 0.0, 0.0, 0.0);
      GL.clearDepth(1.0);
      this.PROJMATRIX = MatrixUtils.get_projection(100, this.canvas.width / this.canvas.height, 1, 1000);
      this.MOVEMATRIX = MatrixUtils.get_I4();
      this.VIEWMATRIX = MatrixUtils.get_I4();
      MatrixUtils.translateZ(this.VIEWMATRIX, -6);
    }

    Scene3D.prototype.add = function(shape) {
      this.shapes.push(shape);
      shape.context = this.context;
      shape.scene = this;
      return this.shapes = this.shapes.sort(function(a, b) {
        if (a.zIndex < b.zIndex) {
          return 1;
        } else if (a.zIndex > b.zIndex) {
          return -1;
        } else {
          return 0;
        }
      });
    };

    Scene3D.prototype.remove = function(shape) {
      return this.shapes.find(function(elem, index, arr) {
        elem.context = null;
        elem.scene = null;
        return arr.splice(index, 1);
      });
    };

    Scene3D.prototype.animationLoop = function(self, appTime) {
      var dt;
      if (self.lastTime) {
        dt = (appTime - self.lastTime) / 1000;
        self.shapes.each(function(elem, index, arr) {
          var cancel, eventArgs, force, newx, newy, newz;
          force = elem.getComposedForce();
          elem.velocity.dx += force.ax * dt;
          elem.velocity.dy += force.ay * dt;
          elem.velocity.dz += force.az * dt;
          newx = elem.location.x + elem.velocity.dx * dt;
          eventArgs = {
            shape: elem,
            x: newx,
            y: elem.location.y,
            z: elem.location.z,
            time: appTime
          };
          cancel = !elem.notify("onmoving", eventArgs);
          if (!cancel) {
            elem.location.x = newx;
          }
          newy = elem.location.y + elem.velocity.dy * dt;
          eventArgs = {
            shape: elem,
            x: elem.location.x,
            y: newy,
            z: elem.location.z,
            time: appTime
          };
          cancel = !elem.notify("onmoving", eventArgs);
          if (!cancel) {
            elem.location.y = newy;
          }
          newz = elem.location.z + elem.velocity.dz * dt;
          eventArgs = {
            shape: elem,
            x: elem.location.x,
            y: elem.location.y,
            z: newz,
            time: appTime
          };
          cancel = !elem.notify("onmoving", eventArgs);
          if (!cancel) {
            elem.location.z = newz;
          }
          return true;
        });
      }
      self.lastTime = appTime;
      return self.invalidate();
    };

    Scene3D.prototype.invalidate = function() {
      var GL;
      GL = this.context;
      GL.enable(GL.DEPTH_TEST);
      GL.depthFunc(GL.LEQUAL);
      GL.clearColor(0.0, 0.0, 0.0, 0.0);
      GL.clearDepth(1.0);
      return this.shapes.each(function(elem, index, arr) {
        return elem.draw();
      });
    };

    return Scene3D;

  })(Observable);

  window.Shape3D = (function(_super) {

    __extends(Shape3D, _super);

    function Shape3D(scene) {
      this.scene = scene;
      Shape3D.__super__.constructor.apply(this, arguments);
      this.size = 100;
      this.rotation = {
        x: 0,
        y: 0,
        z: 0
      };
      this.location = {
        x: 0,
        y: 0,
        z: 0
      };
      this.velocity = {
        dx: 0,
        dy: 0,
        dz: 0
      };
      this.forces = [];
      this.PROJMATRIX = this.scene.PROJMATRIX;
      this.MOVEMATRIX = this.scene.MOVEMATRIX;
      this.VIEWMATRIX = this.scene.VIEWMATRIX;
    }

    Shape3D.prototype.setSize = function(size) {};

    Shape3D.prototype.addForce = function(id, ax, ay, az) {
      var force;
      force = {
        id: id,
        ax: ax,
        ay: ay,
        az: az
      };
      return this.forces.push(force);
    };

    Shape3D.prototype.getForce = function(id) {
      var force;
      force = null;
      this.forces.find(function(elem, index, arr) {
        if (elem.id === id) {
          return force = elem;
        }
      });
      if (force) {
        return force;
      } else {
        return this.addForce(id, 0, 0, 0);
      }
    };

    Shape3D.prototype.setForceAx = function(id, ax) {
      var force;
      force = null;
      return this.forces.find(function(elem, index, arr) {
        if (elem.id === id) {
          return elem.ax = ax;
        }
      });
    };

    Shape3D.prototype.setForceAy = function(id, ay) {
      var force;
      force = null;
      return this.forces.find(function(elem, index, arr) {
        if (elem.id === id) {
          return elem.ay = ay;
        }
      });
    };

    Shape3D.prototype.setForceAz = function(id, az) {
      var force;
      force = null;
      return this.forces.find(function(elem, index, arr) {
        if (elem.id === id) {
          return elem.az = az;
        }
      });
    };

    Shape3D.prototype.removeForce = function(id) {
      this.forces.find(function(elem, index, arr) {
        if (elem.id === id) {
          return arr.splice(index, 1);
        }
      });
      return true;
    };

    Shape3D.prototype.getComposedForce = function() {
      var force;
      force = {
        id: "composed",
        ax: 0,
        ay: 0,
        az: 0
      };
      this.forces.each(function(elem, index, arr) {
        if (elem.ax) {
          force.ax += elem.ax;
        }
        if (elem.ay) {
          force.ay += elem.ay;
        }
        if (elem.az) {
          return force.az += elem.az;
        }
      });
      return force;
    };

    return Shape3D;

  })(Observable);

  window.Cube = (function(_super) {

    __extends(Cube, _super);

    function Cube(scene) {
      Cube.__super__.constructor.call(this, scene);
      this.shader = new Shader(this.scene.context);
      this.shader_vertex_source = "\n\            attribute vec3 position;\n\            uniform mat4 Pmatrix;\n\            uniform mat4 Vmatrix;\n\            uniform mat4 Mmatrix;\n\            attribute vec2 uv;\n\            varying vec2 vUV;\n\            void main(void) { //pre-built function\n\            gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);\n\            vUV=uv;\n\            }";
      this.shader_fragment_source = "\n\            precision mediump float;\n\            uniform sampler2D sampler;\n\            varying vec2 vUV;\n\            \n\            \n\            void main(void) {\n\            gl_FragColor = texture2D(sampler, vUV);\n\            }";
      this.vertexShader = this.shader.addShader(this.shader_vertex_source, this.scene.context.VERTEX_SHADER, "VERTEX");
      this.fragmentShader = this.shader.addShader(this.shader_fragment_source, this.scene.context.FRAGMENT_SHADER, "FRAGMENT");
      this.shader.attachShader(this.vertexShader);
      this.shader.attachShader(this.fragmentShader);
      this.shader.link();
      this._Pmatrix = this.shader.getUniformLocation("Pmatrix");
      this._Vmatrix = this.shader.getUniformLocation("Vmatrix");
      this._Mmatrix = this.shader.getUniformLocation("Mmatrix");
      this._sampler = this.shader.getUniformLocation("sampler");
      this._uv = this.shader.getAttribLocation("uv");
      this._position = this.shader.getAttribLocation("position");
      this.scene.context.enableVertexAttribArray(this._uv);
      this.scene.context.enableVertexAttribArray(this._position);
      this.setSize(100);
      this.cube_faces = [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23];
      this.cube_faces_buffer = this.scene.context.createBuffer();
      this.scene.context.bindBuffer(this.scene.context.ELEMENT_ARRAY_BUFFER, this.cube_faces_buffer);
      this.scene.context.bufferData(this.scene.context.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.cube_faces), this.scene.context.STATIC_DRAW);
    }

    Cube.prototype.setSize = function(size) {
      var actualSize;
      actualSize = size / 100;
      this.cube_vertices = [-actualSize, -actualSize, -actualSize, 0, 0, actualSize, -actualSize, -actualSize, 1, 0, actualSize, actualSize, -actualSize, 1, 1, -actualSize, actualSize, -actualSize, 0, 1, -actualSize, -actualSize, actualSize, 0, 0, actualSize, -actualSize, actualSize, 1, 0, actualSize, actualSize, actualSize, 1, 1, -actualSize, actualSize, actualSize, 0, 1, -actualSize, -actualSize, -actualSize, 0, 0, -actualSize, actualSize, -actualSize, 1, 0, -actualSize, actualSize, actualSize, 1, 1, -actualSize, -actualSize, actualSize, 0, 1, actualSize, -actualSize, -actualSize, 0, 0, actualSize, actualSize, -actualSize, 1, 0, actualSize, actualSize, actualSize, 1, 1, actualSize, -actualSize, actualSize, 0, 1, -actualSize, -actualSize, -actualSize, 0, 0, -actualSize, -actualSize, actualSize, 1, 0, actualSize, -actualSize, actualSize, 1, 1, actualSize, -actualSize, -actualSize, 0, 1, -actualSize, actualSize, -actualSize, 0, 0, -actualSize, actualSize, actualSize, 1, 0, actualSize, actualSize, actualSize, 1, 1, actualSize, actualSize, -actualSize, 0, 1];
      this.cube_vertex_buffer = this.scene.context.createBuffer();
      this.scene.context.bindBuffer(this.scene.context.ARRAY_BUFFER, this.cube_vertex_buffer);
      return this.scene.context.bufferData(this.scene.context.ARRAY_BUFFER, new Float32Array(this.cube_vertices), this.scene.context.STATIC_DRAW);
    };

    Cube.prototype.setTexture = function(url, callback) {
      return this.texture = new Texture(this.scene.context, url, callback);
    };

    Cube.prototype.draw = function() {
      var GL;
      this.shader.use();
      GL = this.scene.context;
      MatrixUtils.set_I4(this.MOVEMATRIX);
      MatrixUtils.rotateZ(this.MOVEMATRIX, MatrixUtils.degToRad(this.rotation.z));
      MatrixUtils.rotateY(this.MOVEMATRIX, MatrixUtils.degToRad(this.rotation.y));
      MatrixUtils.rotateX(this.MOVEMATRIX, MatrixUtils.degToRad(this.rotation.x));
      MatrixUtils.translateZ(this.MOVEMATRIX, MatrixUtils.degToRad(this.location.z));
      MatrixUtils.translateY(this.MOVEMATRIX, MatrixUtils.degToRad(this.location.y));
      MatrixUtils.translateX(this.MOVEMATRIX, MatrixUtils.degToRad(this.location.x));
      GL.uniformMatrix4fv(this._Pmatrix, false, this.PROJMATRIX);
      GL.uniformMatrix4fv(this._Vmatrix, false, this.VIEWMATRIX);
      GL.uniformMatrix4fv(this._Mmatrix, false, this.MOVEMATRIX);
      if (this.texture) {
        GL.activeTexture(GL.TEXTURE0);
        GL.bindTexture(GL.TEXTURE_2D, this.texture.texture);
      }
      GL.bindBuffer(GL.ARRAY_BUFFER, this.cube_vertex_buffer);
      GL.vertexAttribPointer(this._location, 3, GL.FLOAT, false, 4 * (3 + 2), 0);
      GL.vertexAttribPointer(this._uv, 2, GL.FLOAT, false, 4 * (3 + 2), 3 * 4);
      GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, this.cube_faces_buffer);
      GL.drawElements(GL.TRIANGLES, 6 * 2 * 3, GL.UNSIGNED_SHORT, 0);
      return GL.flush();
    };

    return Cube;

  })(window.Shape3D);

}).call(this);
